/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing.Design;
using System.Security.Permissions;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Reflection;

namespace ELE.EnterpriseLibrary.Common.Configuration.Design
{
  /// <summary>
  /// <para>This class is an editor for selecting a property name from a specific type.</para>
  /// <para>This class is used in conjuction with the <see cref="ObjectPropertySelectorUI"/>.</para>
  /// <para>To use it, markup the property to be edited with the attribute
  /// </para>
  ///   <code>
  ///    [System.ComponentModel.Editor(typeof(ObjectPropertySelectorEditor), typeof(UITypeEditor))]
  ///   </code>
  /// <para>And make the class that contains that property implement <see cref="IObjectTypeReferencingObject"/>
  /// to define the type that contains the property. For example:</para>
  /// <code>
  /// using System.ComponentModel;
  /// namespace MyNamespace
  /// {
  ///   public class MyObject : IObjectTypeReferencingObject
  ///   {
  ///     private string property;
  /// 
  ///     [Editor(typeof(ObjectPropertySelectorEditor), typeof(UITypeEditor))]
  ///     public string BoundProperty
  ///     {
  ///       get { return property; }
  ///       set { property = value; }
  ///     }
  ///     public Type GetReferencedType()
  ///     {
  ///       return this.GetType();
  ///     }
  ///     public string GetReferencedTypeName()
  ///     {
  ///       return this.GetReferencedType().Name;
  ///     }
  ///   }
  /// }
  /// </code>
  /// </summary>
  [PermissionSet(SecurityAction.InheritanceDemand, Name = "FullTrust")]
  [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
  public class ObjectPropertySelectorEditor : UITypeEditor
  {
    /// <summary>
    /// Default constructor
    /// </summary>
    public ObjectPropertySelectorEditor()
      : base()
    {
    }
    /// <summary>
    /// Edits the value of the property that has been marked with the <see cref="System.ComponentModel.EditorAttribute"/> as
    /// using the ObjectPropertySelectorEditor by poping up a window of <see cref="ObjectPropertySelectorUI"/>.
    /// </summary>
    /// <param name="context">The context under which the component to be edited lives</param>
    /// <param name="provider">The service provider used to get the <see cref="IWindowsFormsEditorService"/></param>
    /// <param name="value">The value of the property to be edited</param>
    /// <returns>The edited property value</returns>
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
      Debug.Assert(provider != null, "No service provider; we cannot edit the value");
      if (provider != null)
      {
        IWindowsFormsEditorService edSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));

        Debug.Assert(edSvc != null, "No editor service; we cannot edit the value");
        if (edSvc != null)
        {

          IWindowsFormsEditorService service = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
          string currentType = (string) value;
          using (ObjectPropertySelectorUI form = new ObjectPropertySelectorUI(GetObjectType(context)))
          {
            if (service.ShowDialog(form) == DialogResult.OK)
            {
              if (form.SelectedProperty != null)
              {
                currentType = form.SelectedProperty;
              }
            }
          }
          return currentType;
        }
      }
      return value;
    }
    /// <summary>
    /// Edit style set to <see cref="UITypeEditorEditStyle.Modal"/>
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
      return UITypeEditorEditStyle.Modal;
    }

    private Type GetObjectType(ITypeDescriptorContext context)
    {
      if (context.Instance is IObjectTypeReferencingObject)
      {
        IObjectTypeReferencingObject aro = (IObjectTypeReferencingObject)context.Instance;
        return aro.GetReferencedType();
      }
      throw new ApplicationException("Objects using the ObjectPropertyTypeSelectorEditor as the type of ComponentModel.Editor must implement the IObjectTypeReferencingObject interface");
    }
  }
}
